package io.gitee.hefren.hhcache.core;

import com.google.common.base.Strings;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

import java.util.Objects;

import static io.gitee.hefren.hhcache.core.Command.CRLF;
import static io.gitee.hefren.hhcache.core.Command.OK;

/**
 * @Date 2024/6/30
 * @Author lifei
 */
public class HhCacheHandler extends SimpleChannelInboundHandler<String> {

    private static final String CACHE_NULL_FLAG = "-1";
    private static final String CACHE_EMPTY_FLAG = "0";
    private static final String BULK_NULL = ReplyTypeEnum.BULK_STRING.getPrefix() + CACHE_NULL_FLAG + CRLF;
    private static final String BULK_EMPTY = ReplyTypeEnum.BULK_STRING.getPrefix() + CACHE_EMPTY_FLAG + CRLF;
    private static final String ARRAY_NULL = ReplyTypeEnum.ARRAY.getPrefix() + CACHE_NULL_FLAG + CRLF;
    private static final String ARRAY_EMPTY = ReplyTypeEnum.ARRAY.getPrefix() + CACHE_EMPTY_FLAG + CRLF;


    private static final HhCache cache = new HhCache();

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String message) throws Exception {
        System.out.println(Strings.lenientFormat("=====> HhCacheHandler message: %s", message));
        String[] args = message.split(CRLF);
        String comName = args[2].toUpperCase();
        Command command = Commands.getCommand(comName);
        try {
            Reply<?> reply = command.exec(cache, args);
            System.out.println(Strings.lenientFormat("=====> COM [%s], reply: %s", comName, reply));
            writeReply(channelHandlerContext, reply);
        }catch (Exception e) {
            String err = Strings.lenientFormat("COM[%s] : %s", command.name(), e.getMessage());
            writeError(channelHandlerContext, err);
        }
    }

    private void writeReply(ChannelHandlerContext channelHandlerContext, Reply<?> reply) {
        ReplyTypeEnum replyTypeEnum = reply.getReplyTypeEnum();
        if (Objects.equals(replyTypeEnum, ReplyTypeEnum.SIMPLY_STRING)) {
            writeSimpleContext(channelHandlerContext, (String) reply.getValue());
        } else if (Objects.equals(replyTypeEnum, ReplyTypeEnum.BULK_STRING)) {
            writeBulkContext(channelHandlerContext, (String) reply.getValue());
        } else if (Objects.equals(replyTypeEnum, ReplyTypeEnum.INTEGER)) {
            writeInteger(channelHandlerContext, (Integer) reply.getValue());
        } else if (Objects.equals(replyTypeEnum, ReplyTypeEnum.ERROR)) {
            writeError(channelHandlerContext, (String) reply.getValue());
        } else if (Objects.equals(replyTypeEnum, ReplyTypeEnum.ARRAY)) {
            writeArray(channelHandlerContext, (String[]) reply.getValue());
        } else {
            writeSimpleContext(channelHandlerContext, OK);
        }
    }

    private String errorString(String context) {
        return ReplyTypeEnum.ERROR.getPrefix() + context + CRLF;
    }

    private void writeError(ChannelHandlerContext channelHandlerContext, String context) {
        writeByteBuf(channelHandlerContext, errorString(context));
    }

    private String arrayString(Object[] values) {
        StringBuffer sb = new StringBuffer();
        if (Objects.isNull(values)) {
            sb.append(ARRAY_NULL);
        } else if (values.length == 0) {
            sb.append(ARRAY_EMPTY);
        } else {
            sb.append(ReplyTypeEnum.ARRAY.getPrefix());
            sb.append(values.length);
            sb.append(CRLF);
            for (Object value : values) {
                if (Objects.isNull(value)) {
                    sb.append(BULK_NULL);
                } else if (value instanceof String strVal) {
                    sb.append(bulkString(strVal));
                } else if (value instanceof Integer num) {
                    sb.append(integerString(num));
                } else if (value instanceof Object[] subArray) {
                    sb.append(arrayString(subArray));
                } else {
                    sb.append(bulkString(value.toString()));
                }
            }
        }
        return sb.toString();
    }

    private void writeArray(ChannelHandlerContext channelHandlerContext, Object[] values) {
        writeByteBuf(channelHandlerContext, arrayString(values));
    }

    private String integerString(Integer num) {
        if (Objects.isNull(num)) {
            return BULK_NULL;
        } else {
            return ReplyTypeEnum.INTEGER.getPrefix() + num + CRLF;
        }
    }

    private void writeInteger(ChannelHandlerContext channelHandlerContext, Integer num) {
        writeByteBuf(channelHandlerContext, integerString(num));
    }

    private String simpleString(String context) {
        String result;
        if (Objects.isNull(context)) {
            result = BULK_NULL;
        } else if (context.isEmpty()) {
            result = BULK_EMPTY;
        } else {
            result = ReplyTypeEnum.SIMPLY_STRING.getPrefix() + context + CRLF;
        }
        return result;
    }

    private void writeSimpleContext(ChannelHandlerContext channelHandlerContext, String context) {
        writeByteBuf(channelHandlerContext, simpleString(context));
    }

    private String bulkString(String context) {
        String result;
        if (Objects.isNull(context)) {
            result = BULK_NULL;
        } else if (context.isEmpty()) {
            result = BULK_EMPTY;
        } else {
            result = ReplyTypeEnum.BULK_STRING.getPrefix() + context.getBytes().length + CRLF + context + CRLF;
        }
        return result;
    }

    private void writeBulkContext(ChannelHandlerContext channelHandlerContext, String context) {
        writeByteBuf(channelHandlerContext, bulkString(context));
    }

    private void writeByteBuf(ChannelHandlerContext channelHandlerContext, String message) {
        ByteBuf byteBuf = Unpooled.buffer();
        byteBuf.writeBytes(message.getBytes());
        channelHandlerContext.writeAndFlush(byteBuf);
    }
}
